package com.example.picofteacherend.auth;

import cn.hutool.core.util.StrUtil;
import com.example.picofteacherend.entity.Role;
import com.example.picofteacherend.entity.User;
import com.example.picofteacherend.service.UserService;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Set;

/**
 * @author: zjb
 * @description:
 * @date: 2023/3/19 19:13
 * @version: 1.0
 */
@Slf4j
public class CustomRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * Shiro 的权限授权是通过继承AuthorizingRealm抽象类，重载doGetAuthorizationInfo();
     * 当访问到页面的时候，链接配置了相应的权限或者 Shiro 标签才会执行此方法否则不会执行，
     * 所以如果只是简单的身份认证没有权限的控制的话，那么这个方法可以不进行实现，直接返回 null 即可。
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        String no = (String) principals.getPrimaryPrincipal();
        User user = userService.findByUserNo(no);
        Set<String> roles = new HashSet<>();
        Set<String> permissions = new HashSet<>();
        if (user.getRole().equals(Role.ADMIN.toString())) {
            roles.add("admin");
            permissions.add("user:create");
            permissions.add("user:update");
            permissions.add("user:delete");
        } else if (user.getRole().equals(Role.TEACHER.toString())) {
            roles.add("teacher");
            permissions.add("course:create");
            permissions.add("course:update");
            permissions.add("course:delete");
        } else if (user.getRole().equals(Role.STUDENT.toString())) {
            roles.add("student");
            permissions.add("course:view");
        }
        authorizationInfo.setRoles(roles);
        authorizationInfo.setStringPermissions(permissions);
        return authorizationInfo;
    }

    /**
     * 在认证、授权内部实现机制中都有提到，最终处理都将交给Real进行处理。
     * 因为在 Shiro 中，最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。
     * 通常情况下，在 Realm 中会直接从我们的数据源中获取 Shiro 需要的验证信息。
     * 可以说，Realm 是专用于安全框架的 DAO. Shiro 的认证过程最终会交由 Realm 执行，
     * 这时会调用 Realm 的getAuthenticationInfo(token)方法。
     * <p>
     * 该方法主要执行以下操作:
     * <p>
     * 1、检查提交的进行认证的令牌信息
     * 2、根据令牌信息从数据源(通常为数据库)中获取用户信息
     * 3、对用户信息进行匹配验证。
     * 4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例。
     * 5、验证失败则抛出AuthenticationException异常信息。
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String token = (String) authenticationToken.getCredentials();
        String no = null;
        try {
            no = JwtUtil.getClaimField(token, JwtUtil.ACCOUNT);
            User user = userService.findByUserNo(no);
            if (user == null) {
                log.error("用户不存在");
                return null;
            }
            boolean verify = JwtUtil.verify(token, no, user.getPassword());
            if (!verify) {
                log.error("Token校验不正确");
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                no,token,getName());
        return authenticationInfo;
    }

}

